/* ***** BEGIN LICENSE BLOCK *****
 *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Seoprofesional
 *
 * The Initial Developer of the Original Code is
 * Petr Blahoš.
 * Portions created by the Initial Developer are Copyright (C) 2009
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 * 
 * ***** END LICENSE BLOCK ***** */

function LOG(msg) {
  var consoleService = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService);
  consoleService.logStringMessage(msg);
}

function DUMP_MEMBERS(o) {
	var s = "" + o + " members:";
	for (var i in o) {
		s+= i + ",";
	}
	LOG(s);
}

function $(id) {
	return document.getElementById(id);
}
function $sa(id, name, val) {
	document.getElementById(id).setAttribute(name, val);
}
function $ga(id, name, val) {
	return document.getElementById(id).getAttribute(name);
}
function $clear(el) {
	while(null!=el.firstChild) {
		el.removeChild(el.firstChild);
	}
}
function $addmenu(menu, label, f) {
	var el = document.createElement("menuitem");
	el.setAttribute("label", label);
	el.setAttribute("oncommand", f);
	menu.appendChild(el);
}
function $svgtext(chart, x, y, val) {
	var el = document.createElementNS("http://www.w3.org/2000/svg", "text");
	el.setAttribute("x", x);
	el.setAttribute("y", y);
	var t = document.createTextNode(val);
	el.appendChild(t);
	chart.appendChild(el);
}
function $svgline(chart, x1, y1, x2, y2) {
	var el = document.createElementNS("http://www.w3.org/2000/svg", "line");
	el.setAttribute("x1", x1);
	el.setAttribute("y1", y1);
	el.setAttribute("x2", x2);
	el.setAttribute("y2", y2);
	chart.appendChild(el);
}
function $svgrect(chart, x1, y1, x2, y2) {
	var el = document.createElementNS("http://www.w3.org/2000/svg", "rect");
	el.setAttribute("x", x1);
	el.setAttribute("y", y1);
	el.setAttribute("width", x2-x1);
	el.setAttribute("height", 100-y1);
	chart.appendChild(el);
}

Components.utils.import("resource://seoprofesional/seopdb.jsm");

const SEOCZ_ATTR_BASE = "x-seop"

const SEOP_BAR_SIZE = 50;

var dummy_fetcher = {
enabled: 1,
name: "dummy",		// the rank's name
shortname: "D",
id: 1,			// the rank's ID
refresh: 0,
max: 200,
fetch: function(tab, uri) {
	this.onreadystatechange(null, null, tab, uri);
},
onreadystatechange: function(req, evt, tab, uri) {
	var rank = Math.floor(Math.random()*200);
	tab.setAttribute("x-seop-value-" + this.name, rank);
	seopdb.add_rank(uri, this.id, rank);
	tab.setAttribute("x-seop-status-" + this.name, 200);
	if (gBrowser.selectedBrowser==tab) {
		seop.tabSelected(null);
	}
},
is_my_id: function(id) {
	var prefix = "seop-"+this.name;
	return prefix==id.substring(0, prefix.length);
},
_x_sentinel_: 0
};

var srank_fetcher = {
enabled: 1,
name: "srank",		// the rank's name
shortname: "S", 
id: 2,			// the rank's ID
refresh: 0,
max: 100,
fetch: function(tab, uri) {
	var req = new XMLHttpRequest();
	req.onreadystatechange = function(evt) {
		srank_fetcher.onreadystatechange(req, evt, tab, uri);
	};
	var body = '<?xml version="1.0" encoding="UTF-8"?>' +
	    '<methodCall>' +
		'<methodName>getRank</methodName>' +
		'<params>' +
		    '<param>' +
			'<value>' +
			    '<string>0</string>' +
			'</value>' +
		    '</param>' +
		    '<param>' +
			'<value>' +
			    '<string>' + uri + '</string>' +
			'</value>' +
		    '</param>' +
		    '<param>' +
			'<value>' +
			    '<i4>0</i4>' +
			'</value>' +
		    '</param>' +
		'</params>' +
	    '</methodCall>';	
	req.open("POST", "http://srank.seznam.cz/RPC2");
	req.setRequestHeader("Content-Type", "text/xml");
	req.setRequestHeader("Host", "srank.seznam.cz");
	req.setRequestHeader("Connection", "Close");
	//req.setRequestHeader("", "");
	req.send(body);
	
},
onreadystatechange: function(req, evt, tab, uri) {
	if (tab.contentDocument.baseURIObject.spec!=uri) {
		LOG("The tab content has changed. Aborting the request.");
		req.abort();
		return ;
	}
	if (4==req.readyState) {  
		if(200==req.status) {
			var rank = null;
			var r = /<name>rank<\/name>\s*<value><i[a-z0-9]*>([^<]*)<\/i/gi;
			var m = r.exec(req.responseText);
			if (null!=m) {
			    rank = Math.floor(100*m[1]/255);
			    m = r.exec();
			    while (null!=m) {
				m = r.exec();
			    }
			}
			if (null==rank) {
				tab.setAttribute("x-seop-value-" + this.name, 0);
				LOG("error fetching RANK for " + uri + "\n|" + req.responseText + "|");
			} else {
				tab.setAttribute("x-seop-value-" + this.name, rank);
				seopdb.add_rank(uri, this.id, rank);
			}
		}
		tab.setAttribute("x-seop-status-" + this.name, req.status);
		LOG("setting status: " + SEOCZ_ATTR_BASE + this.name + "-status --> " + req.status);
		if (gBrowser.selectedBrowser==tab) {
			seop.tabSelected(null);
		}
	}
},
is_my_id: function(id) {
	var prefix = "seop-"+this.name;
	return prefix==id.substring(0, prefix.length);
},
_x_sentinel_: 0
};


var pagerank_fetcher = {
enabled: 1,
name: "pagerank",		// the rank's name
shortname: "G",
id: 3,			// the rank's ID
refresh: 0,
max: 10,
get_pr_url: function(url_in) {
	//from: http://www.7is7.com/software/bookmarklets/googlepagerank.html
	var zF = function(a,b) {
		var z=parseInt(80000000,16);
		if(z&a){ a=a>>1;a&=~z;a|=0x40000000;a=a>>(b-1);
		}else{ a=a>>b; }
		return(a);
	},
	mix=function(a,b,c){
		a-=b;a-=c;a^=(zF(c,13));b-=c;b-=a;b^=(a<<8);c-=a;c-=b;c^=(zF(b,13));
		a-=b;a-=c;a^=(zF(c,12));b-=c;b-=a;b^=(a<<16);c-=a;c-=b;c^=(zF(b,5));
		a-=b;a-=c;a^=(zF(c,3));b-=c;b-=a;b^=(a<<10);c-=a;c-=b;c^=(zF(b,15));
		return(new Array((a),(b),(c)));
	},
	GoogleCH=function(url,length){
		if(arguments.length==1)length=url.length;
		var a=0x9E3779B9,b=0x9E3779B9,c=0xE6359A60,k=0,len=length,mx=new Array();
		while(len>=12){
			a+=(url[k+0]+(url[k+1]<<8)+(url[k+2]<<16)+(url[k+3]<<24));
			b+=(url[k+4]+(url[k+5]<<8)+(url[k+6]<<16)+(url[k+7]<<24));
			c+=(url[k+8]+(url[k+9]<<8)+(url[k+10]<<16)+(url[k+11]<<24));
			mx=mix(a,b,c);a=mx[0];b=mx[1];c=mx[2];k+=12;len-=12;
		}
		c+=length;
		switch(len){
		case 11:c+=url[k+10]<<24;
		case 10:c+=url[k+9]<<16;
		case 9:c+=url[k+8]<<8;
		case 8:b+=(url[k+7]<<24);
		case 7:b+=(url[k+6]<<16);
		case 6:b+=(url[k+5]<<8);
		case 5:b+=(url[k+4]);
		case 4:a+=(url[k+3]<<24);
		case 3:a+=(url[k+2]<<16);
		case 2:a+=(url[k+1]<<8);
		case 1:a+=(url[k+0]);
		}
		mx=mix(a,b,c);
		if(mx[2]<0)return(0x100000000+mx[2]);
		else{ return(mx[2]); }
	},
	strord=function(string){
		var result=new Array();
		for(i=0;i<string.length;i++){
			result[i]=string[i].charCodeAt(0);
		}
		return(result);
	};
	return 'http://www.google.com/search?client=navclient-auto&ch=6'+GoogleCH(strord('info:'+url_in))+'&features=Rank&q=info:'+encodeURIComponent(url_in);
	//return '/search?client=navclient-auto&ch=6'+GoogleCH(strord('info:'+url_in))+'&features=Rank&q=info:'+encodeURIComponent(url_in);
},
fetch: function(tab, uri) {
	var req = new XMLHttpRequest();
	req.onreadystatechange = function(evt) {
		pagerank_fetcher.onreadystatechange(req, evt, tab, uri);
	};
	LOG("pagerank request:" + pagerank_fetcher.get_pr_url(uri) + "<");
	req.open("GET", pagerank_fetcher.get_pr_url(uri));
	//req.setRequestHeader("Host", "www.google.com");
	req.setRequestHeader("Connection", "Close");
	req.setRequestHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.6) Gecko/20060728 Firefox/1.5");
	//req.setRequestHeader("", "");
	req.send(null);
	
},
onreadystatechange: function(req, evt, tab, uri) {
	if (tab.contentDocument.baseURIObject.spec!=uri) {
		LOG("The tab content has changed. Aborting the request.");
		req.abort();
		return ;
	}
	if (4==req.readyState) {  
		if(200==req.status) {
			var rank = null;
			var t = req.responseText;
			if (t.lastIndexOf(':')!=-1) {
				rank = parseInt(t.substring(t.lastIndexOf(':')+1));
			}
			if (null==rank) {
				tab.setAttribute("x-seop-value-" + this.name, 0);
				LOG("error fetching PAGE RANK for " + uri + "\n>" + req.responseText + "<");
			} else {
				tab.setAttribute("x-seop-value-" + this.name, rank);
				seopdb.add_rank(uri, this.id, rank);
			}
		}
		tab.setAttribute("x-seop-status-" + this.name, req.status);
		LOG("setting status: " + SEOCZ_ATTR_BASE + this.name + "-status --> " + req.status);
		if (gBrowser.selectedBrowser==tab) {
			seop.tabSelected(null);
		}
	}
},
is_my_id: function(id) {
	var prefix = "seop-"+this.name;
	return prefix==id.substring(0, prefix.length);
},
_x_sentinel_: 0
};
var seop = {
/***
Changing the way how the internal state is handled. Now, whenever 
we have got any event, we just call tabSelected, which will take
care of the window. 
First of all, it checks, whether the active uri is the same as uri
set to the attribute "x-seop-uri". If it is not, it will initiate 
the ranks fetching. Otherwise, the rank fetching initialization 
is skipped.
After that the display of the ranks is refreshed.
***/
onLoad: function() {
	// Listen for webpage loads
	gBrowser.addProgressListener(seop_urlBarListener, Components.interfaces.nsIWebProgress.NOTIFY_LOCATION);

	gBrowser.tabContainer.addEventListener("TabSelect", seop.tabSelected, false);
	//document.getElementById("appcontent").addEventListener("DOMContentLoaded", seop.onPageLoaded, true);
	window.addEventListener("pageshow", seop.onPageLoaded, true);

	this.disptype = 3;
	this.fetchers = [ dummy_fetcher, srank_fetcher, pagerank_fetcher ];

	this.prefs = Components.classes["@mozilla.org/preferences-service;1"]
		.getService(Components.interfaces.nsIPrefService)
		.getBranch("extensions.seoprofesional.");
	this.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
	this.loadprefs();
	this.prefs.addObserver("", this, false);

	this.strings = $("seop-strings");
	this.sb_main = $("seop-main");
	this.sb_write = $("seop-write")

	seop.fillContainer();
	seop.globalToggle(true);
	this.initialized = true;
},
onUnLoad: function() {
	gBrowser.removeProgressListener(seop_urlBarListener);

	//document.getElementById("appcontent").removeEventListener("DOMContentLoaded", seop.onPageLoaded, true);
	window.removeEventListener("pageshow", seop.onPageLoaded, true)
	gBrowser.tabContainer.removeEventListener("TabSelect", seop.tabSelected, false);

	this.prefs.removeObserver("", this);
},
loadprefs: function() {
	for (var i = 0;i<seop.fetchers.length;i++) {
		var f = seop.fetchers[i];
		f.enabled = 1;
		f.refresh = 0;
		try {
			f.enabled = this.prefs.getBoolPref(f.name);
			f.refresh = this.prefs.getIntPref(f.name + ".refresh");
		} catch (e) {
			LOG("loadprefs: caught on " + f.name + ":" + e);
		}
	}
	try {
		this.disptype = this.prefs.getIntPref("disptype");
	} catch (e) {
	}
},
observe: function(subject, topic, data) {
	LOG("observe:" + subject + "," + topic + "," + data + "<");
	if ("nsPref:changed"!=topic) {
		return;
	}
	for (var i = 0;i<seop.fetchers.length;i++) {
		var f = seop.fetchers[i];
		if (f.name==data) {
			f.enabled = this.prefs.getBoolPref(data);
			this.fillContainer();
			LOG("FETCHER " + f.name + " dis/enABLED:" + f.enabled);
			return ;
		} else if (f.name + ".refresh"==data) {
			f.refresh = this.prefs.getIntPref(data);
			LOG("FETCHER " + f.name + " refresh:" + f.refresh);
			return ;
		}
	}
	if ("disptype"==data) {
		this.disptype = this.prefs.getIntPref("disptype");
		this.fillContainer();
	}
},
globalToggle: function(s) {
	this.on = s;
	this.sb_main.src = s ? "chrome://seoprofesional/skin/e.png" : "chrome://seoprofesional/skin/d.png";
	//FIXME: When we turn on, should we start fetching rank for current page? Or even for all pages?
},
mainClick: function(event) {
	if (0==event.button) {
		this.globalToggle(!this.on);
	}
},
fillContainer: function() {
	var el = $("seop-container");
	$clear(el);
	for (var i = 0;i<seop.fetchers.length;i++) {
		var f = seop.fetchers[i];
		if (f.enabled) {
			var x;
			if (this.disptype & 1) {
				x = document.createElement("label");
				x.setAttribute("value", "");
				x.setAttribute("id", "seop-" + f.name);
				x.setAttribute("tooltip", "seop-tip");
				el.appendChild(x);
			}
			if (this.disptype & 2) {
				x = document.createElement("hbox");
				x.setAttribute("id", "seop-" + f.name + "-f");
				x.setAttribute("class", "seop-full-bar")
				x.setAttribute("width", 0);
				x.setAttribute("tooltip", "seop-tip");
				el.appendChild(x);
				x = document.createElement("hbox");
				x.setAttribute("id", "seop-" + f.name + "-e");
				x.setAttribute("class", "seop-empty-bar")
				x.setAttribute("width", SEOP_BAR_SIZE);
				x.setAttribute("tooltip", "seop-tip");
				el.appendChild(x);
			}
		}
	}
	this.tabSelected();
},
onMenuItemCommand: function(e) {
	LOG("Menu Item Command...");
},
setBars: function (f, val) {
	if (0!=val) {
		val = Math.floor(SEOP_BAR_SIZE*val/f.max);
	}
	$("seop-" + f.name + "-f").setAttribute("width", val);
	$("seop-" + f.name + "-e").setAttribute("width", SEOP_BAR_SIZE-val);
},
updateStatus: function (f, uri) {
	var st = gBrowser.selectedBrowser.getAttribute("x-seop-status-" + f.name);
	if (200==st) {
		var val = gBrowser.selectedBrowser.getAttribute("x-seop-value-" + f.name);
		if (1 & this.disptype) {
			$sa("seop-"+f.name, "value", f.shortname + ":" + val);
		}
		if (2 & this.disptype) {
			this.setBars(f, val);
		}
	} else {
		var v = seopdb.get_rank(uri, f.id);
		if (null==v) {
			v = 0;
		}
		if (1 & this.disptype) {
			$sa("seop-"+f.name, "value", f.shortname + ": (" + v + ")");
		}
		if (2 & this.disptype) {
			this.setBars(f, v);
		}
	}
},
initFetch: function(tab, uri) {
	if (!seop.on) {
		LOG("SEOP is OFF " + uri);
		return ;
	}
	tab.setAttribute("x-seop-uri", uri);
	for (var i = 0;i<seop.fetchers.length;i++) {
		f = seop.fetchers[i];
		tab.setAttribute("x-seop-status-" + f.name, 0);
		tab.setAttribute("x-seop-value-" + f.name, 0);
		if (f.enabled) {
			seop.fetchers[i].fetch(tab, uri);
		}
	}
},
tabSelected: function (event) {
	// !!! event CAN be null !!!
	var uri = gBrowser.selectedBrowser.contentDocument.baseURIObject.spec;
	var stored_uri = gBrowser.selectedBrowser.getAttribute("x-seop-uri");

	if (""!=seopdb.is_whitelisted(uri)) {
		seop.sb_write.setAttribute("src", "chrome://seoprofesional/skin/we.png");
	} else {
		seop.sb_write.setAttribute("src", "chrome://seoprofesional/skin/wd.png");
	}

	if (!this.isRankable(uri)) {
		$sa("seop-container", "hidden", true);
		return ;
	}

	if (uri!=stored_uri) {
		seop.initFetch(gBrowser.selectedBrowser, uri);
	}

	$sa("seop-container", "hidden", false);
	for (var i = 0;i<seop.fetchers.length;i++) {
		if (seop.fetchers[i].enabled) {
			seop.updateStatus(seop.fetchers[i], uri);
		}
	}
},
onMenuShowing: function (event) {
	if ("seop-menu"!=event.target.id) {
		return true;
	}
	var uri = gBrowser.selectedBrowser.contentDocument.baseURIObject.spec;
	if (!this.isRankable(uri)) {
		$sa("seop-menu-enable", "hidden", true);
		$sa("seop-menu-disable", "hidden", true);
	} else {
		var wl = seopdb.is_whitelisted(uri);
		if (""==wl) {
			$sa("seop-menu-enable", "hidden", false);
			$sa("seop-menu-disable", "hidden", true);
			var menu = $("seop-sub-enable");
			$clear(menu);
			$addmenu(menu, uri, "seop.enableUri('"+uri+"')");
		} else {
			$sa("seop-menu-enable", "hidden", true);
			$sa("seop-menu-disable", "hidden", false);
			var menu = $("seop-sub-disable");
			$clear(menu);
			$addmenu(menu, wl, "seop.disableUri('"+wl+"')");
		}
	}
},
enableUri: function(uri) {
	seopdb.whitelist(uri);
	seopdb.tabSelected(null);
},
disableUri: function(uri) {
	seopdb.un_whitelist(uri);
	seopdb.tabSelected(null);
},
onPageLoaded: function (event) {
	var doc = event.originalTarget;
	var uri = doc.baseURIObject.spec;

	var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator);
	var browserEnumerator = wm.getEnumerator("navigator:browser");
	var found = 0;
	//FIXME: this is rather a stupid way to get associated browser for the loaded document. 
	// Is there a better way?!?
	while (!found && browserEnumerator.hasMoreElements()) {
		var browserWin = browserEnumerator.getNext();
		var tabbrowser = browserWin.getBrowser();
		// Check each tab of this browser instance
		var numTabs = tabbrowser.browsers.length;
		for(var index=0; index<numTabs; index++) {
			var currentBrowser = tabbrowser.getBrowserAtIndex(index);
			if (currentBrowser.contentDocument==doc) {
				found = currentBrowser;
			}
		}
	}
	if (found) {
		if (gBrowser.selectedBrowser==found) {
			seop.tabSelected(null);
		} else if (this.isRankable(uri)) {
			var stored_uri = found.getAttribute("x-seop-uri");
			if (stored_uri!=uri) {
				seop.initFetch(found, uri);
			}
		}
	} else {
		// log this again when you get rid of frames, iframes and such
		//LOG("overlay.js#1:THIS SHOULD NOT HAPPEN  !!!" + uri);
	}
},
isRankable: function(uri) {
	return "http://"==uri.substring(0, 7) || "https://"==uri.substring(0, 8);
},
tipShowing: function(event) {
	var ttt = 1;
	try {
		ttt = this.prefs.getIntPref("tttype");
	} catch (e) {
		LOG("EXCEPTION:" + e);
	}
	switch(ttt) {
	case 2:		this.tipShowing2(event);		break;
	case 3:		this.tipShowing3(event);		break;
	case 4:		this.tipShowing4(event);		break;
	default:	this.tipShowing1(event);		break;
	}
},
tipShowing3: function(event) {
	var uri = gBrowser.selectedBrowser.contentDocument.baseURIObject.spec;
	if (!seopdb.is_whitelisted(uri)) {
		return false;
	}
	$("outer").hidden = true;
	var i;
	var fetcher = null;
	for (i = 0;i<seop.fetchers.length;i++) {
		if (seop.fetchers[i].is_my_id(document.tooltipNode.id)) {
			fetcher = seop.fetchers[i];
			break;
		}
	}
	if (null==fetcher) {
		return false;
	}

	$("tthead").value = fetcher.name;
	$("ttfoot").value = document.tooltipNode.id;

	var tl = seopdb.get_simple_tl(uri, fetcher.id);
	if (0==tl.length) {
		return true;
	}
	$("outer").hidden = false;
	var i0 = 0;
	if (tl.length>10) {
		i0 = tl.length-10;
	}
	var max = 1;
	for (i = i0;i<tl.length;i++) {
		if (tl[i][1]>max)	max = tl[i][1];
	}
	var chartlines = $("ttchartlines");
	var chartbars = $("ttchartbars");
	var texts = $("tttexts");
	$clear(chartlines);
	$clear(chartbars);
	$clear(texts);
	var last = [null, null];
	var counter = 0;
	$svgtext(texts, -25, 5, "" + max);
	for (i = i0;i<tl.length;i++) {
		if (i<tl.length-1) {
			$svgrect(chartbars, counter*100, 100-100/max*tl[i][1], (counter+1)*100, 100);
			$svgline(chartlines, counter*100, 100-100/max*tl[i][1], (counter+1)*100, 100-100/max*tl[i][1]);
		}
		var d = tl[i][0];
		$svgtext(texts, counter*100-30, 120, "" + d.getFullYear() + "/" + (1+d.getMonth()) + "/" + d.getDate());
		LOG("" + d.getFullYear() + "/" + (1+d.getMonth()) + "/" + d.getDate() + " ==> " + tl[i][1]);
		counter++;
	}
	return true;
},
tipShowing4: function(event) {
	var uri = gBrowser.selectedBrowser.contentDocument.baseURIObject.spec;
	if (!seopdb.is_whitelisted(uri)) {
		return false;
	}
	$("outer").hidden = true;
	var i;
	var fetcher = null;
	for (i = 0;i<seop.fetchers.length;i++) {
		if (seop.fetchers[i].is_my_id(document.tooltipNode.id)) {
			fetcher = seop.fetchers[i];
			break;
		}
	}
	if (null==fetcher) {
		return false;
	}

	$("tthead").value = fetcher.name;
	$("ttfoot").value = document.tooltipNode.id;

	var tl = seopdb.get_simple_tl(uri, fetcher.id);
	if (0==tl.length) {
		return true;
	}
	$("outer").hidden = false;
	var i0 = 0;
	if (tl.length>10) {
		i0 = tl.length-10;
	}
	var max = 1;
	for (i = i0;i<tl.length;i++) {
		if (tl[i][1]>max)	max = tl[i][1];
	}
	var chartlines = $("ttchartlines");
	var chartbars = $("ttchartbars");
	var texts = $("tttexts");
	$clear(chartlines);
	$clear(chartbars);
	$clear(texts);
	var last = [null, null];
	var counter = 0;
	$svgtext(texts, -25, 5, "" + max);
	for (i = i0;i<tl.length;i++) {
		if (i<tl.length-1) {
			$svgline(chartlines, counter*100, 100-100/max*tl[i][1], (counter+1)*100, 100-100/max*tl[i][1]);
		}
		var d = tl[i][0];
		$svgtext(texts, counter*100-30, 120, "" + d.getFullYear() + "/" + (1+d.getMonth()) + "/" + d.getDate());
		LOG("" + d.getFullYear() + "/" + (1+d.getMonth()) + "/" + d.getDate() + " ==> " + tl[i][1]);
		counter++;
	}
	return true;
},
tipShowing1: function(event) {
	var uri = gBrowser.selectedBrowser.contentDocument.baseURIObject.spec;
	if (!seopdb.is_whitelisted(uri)) {
		return false;
	}
	$("outer").hidden = true;
	var i;
	var fetcher = null;
	for (i = 0;i<seop.fetchers.length;i++) {
		if (seop.fetchers[i].is_my_id(document.tooltipNode.id)) {
			fetcher = seop.fetchers[i];
			break;
		}
	}
	if (null==fetcher) {
		return false;
	}

	$("tthead").value = fetcher.name;
	$("ttfoot").value = document.tooltipNode.id;

	var tl = seopdb.get_tl(uri, fetcher.id);
	if (0==tl.length) {
		return true;
	}
	$("outer").hidden = false;
	var i0 = 0;
	if (tl.length>10) {
		i0 = tl.length-10;
	}
	var max = 1;
	for (i = i0;i<tl.length;i++) {
		if (tl[i][1]>max)	max = tl[i][1];
	}
	var chartlines = $("ttchartlines");
	var chartbars = $("ttchartbars");
	var texts = $("tttexts");
	$clear(chartlines);
	$clear(chartbars);
	$clear(texts);
	var last = [null, null];
	var counter = -1;
	$svgtext(texts, -25, 5, "" + max);
	for (i = i0;i<tl.length;i++) {
		var show_text = 1;
		if (null!=last[0]) {
			if (last[0]==tl[i][0]) {
				$svgrect(chartbars, counter*100, 100-100/max*last[1], counter*100, 100);
				$svgline(chartlines, counter*100, 100-100/max*last[1], counter*100, 100-100/max*tl[i][1]);
				show_text = 0;
			} else if (last[1]==tl[i][1]) {
				$svgrect(chartbars, counter*100, 100-100/max*last[1], (counter+1)*100, 100);
				$svgline(chartlines, counter*100, 100-100/max*last[1], (counter+1)*100, 100-100/max*tl[i][1]);
			}
		}
		last = tl[i];
		if (show_text) {
			var d = tl[i][0];
			$svgtext(texts, (counter+1)*100-30, 120, "" + d.getFullYear() + "/" + (1+d.getMonth()) + "/" + d.getDate());
			counter++;
		}
	}

	return true;
},
tipShowing2: function(event) {
	var uri = gBrowser.selectedBrowser.contentDocument.baseURIObject.spec;
	if (!seopdb.is_whitelisted(uri)) {
		return false;
	}
	$("outer").hidden = true;
	var i;
	var fetcher = null;
	for (i = 0;i<seop.fetchers.length;i++) {
		if (seop.fetchers[i].is_my_id(document.tooltipNode.id)) {
			fetcher = seop.fetchers[i];
			break;
		}
	}
	if (null==fetcher) {
		return false;
	}

	$("tthead").value = fetcher.name;
	$("ttfoot").value = document.tooltipNode.id;

	var tl = seopdb.get_tl(uri, fetcher.id);
	if (0==tl.length) {
		return true;
	}
	$("outer").hidden = false;
	var i0 = 0;
	if (tl.length>10) {
		i0 = tl.length-10;
	}
	var max = 1;
	for (i = i0;i<tl.length;i++) {
		if (tl[i][1]>max)	max = tl[i][1];
	}
	var chartlines = $("ttchartlines");
	var chartbars = $("ttchartbars");
	var texts = $("tttexts");
	$clear(chartlines);
	$clear(chartbars);
	$clear(texts);
	var last = [null, null];
	var counter = -1;
	$svgtext(texts, -25, 5, "" + max);
	for (i = i0;i<tl.length;i++) {
		var show_text = 1;
		if (null!=last[0]) {
			if (last[0]==tl[i][0]) {
				$svgline(chartlines, counter*100, 100-100/max*last[1], counter*100, 100-100/max*tl[i][1]);
				show_text = 0;
			} else if (last[1]==tl[i][1]) {
				$svgline(chartlines, counter*100, 100-100/max*last[1], (counter+1)*100, 100-100/max*tl[i][1]);
			}
		}
		last = tl[i];
		if (show_text) {
			var d = tl[i][0];
			$svgtext(texts, (counter+1)*100-30, 120, "" + d.getFullYear() + "/" + (1+d.getMonth()) + "/" + d.getDate());
			counter++;
		}
	}

	return true;
},
_x_sentinel_: 0
};

var seop_urlBarListener = {
  QueryInterface: function(aIID) {
   if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
       aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
       aIID.equals(Components.interfaces.nsISupports))
     return this;
   throw Components.results.NS_NOINTERFACE;
  },

  onLocationChange: function(aProgress, aRequest, aURI) {
    seop.tabSelected(null);
  },

  onStateChange: function(aWebProgress, aRequest, aFlag, aStatus) {/*
   // If you use myListener for more than one tab/window, use
   // aWebProgress.DOMWindow to obtain the tab/window which triggers the state change
   if(aFlag & STATE_START)
   {
     // This fires when the load event is initiated
   }
   if(aFlag & STATE_STOP)
   {
     // This fires when the load finishes
   }*/
  },

  onProgressChange: function(a, b, c, d, e, f) {},
  onStatusChange: function(a, b, c, d) {},
  onSecurityChange: function(a, b, c) {}
};

window.addEventListener("load", function(e) { seop.onLoad(e); }, false);
window.addEventListener("unload", function(e) { seop.onUnLoad(e); }, false);

